package comp6231.project.replica.kelvin;

import java.io.File;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import comp6231.project.common.ReplicaState;

/**
 * Class represents data structure of Customer records keeping track items and
 * their amount bought by each Customer.
 * @author Kelvin Iu
 *
 */
public class CustomerRecords {
	
	private static final int HASHTABLE_SIZE = 1000;
	private Lock[] locks = new ReentrantLock[HASHTABLE_SIZE];
	private HashMap<String, ItemsList>[] records = new HashMap[HASHTABLE_SIZE];
	
	private String storageDir = "";
	
	public CustomerRecords() {
		init();
	}
	
	private void init() {
		for (int i = 0; i < HASHTABLE_SIZE; i++) {
			locks[i] = new ReentrantLock();
			records[i] = new HashMap<String, ItemsList>();
		}		
	}
	
	private int getLocksSlot(String key) {
		int id = 0;
		try {
			id = Integer.parseInt(key);
		} catch (NumberFormatException ex) {			
		}
		int slot = id % HASHTABLE_SIZE;
		return slot;
	}

	/**
	 * Specify directory to store customer records
	 * @param dir Directory
	 */
	public void setStorageDir(String dir) {
		if (dir != null && dir.length() > 0) {
			storageDir = dir;
			if (!(new File(storageDir)).isDirectory()) {
				(new File(storageDir)).mkdirs();
			}
		}
	}
	
	/**
	 * Return number of items bought by the given Customer and item
	 * @param customerID 6-character Customer ID
	 * @param itemID 4-digit Item ID
	 * @return Number of items
	 */
	public int getNumOfItemBought(String customerID, String itemID) {
		int slot = getLocksSlot(customerID);
		int count = 0;
		locks[slot].lock();
		if (records[slot].containsKey(customerID)) {
			count = records[slot].get(customerID).getCount(itemID);
		}
		locks[slot].unlock();
		return count;
	}
	
	/**
	 * Add a sale record
	 * @param customerID 6-character Customer ID
	 * @param itemID 4-digit Item ID
	 * @param numberOfItem Number of items
	 */
	public void addSaleRecord(String customerID, String itemID, int numberOfItem) {
		int slot = getLocksSlot(customerID);
		locks[slot].lock();
		ItemsList l = records[slot].get(customerID);
		if (l == null) {
			l = new ItemsList();
			records[slot].put(customerID, l);
		}
		l.lock(itemID);
		l.setCount(itemID, l.getCount(itemID) + numberOfItem);
		l.unlock(itemID);
		l.save(storageDir + File.separator + customerID);
		locks[slot].unlock();
	}
	
	/**
	 * Add a return record
	 * @param customerID 6-character Customer ID
	 * @param itemID 4-digit Item ID
	 * @param numberOfItem Number of items
	 * @return true if successful, false otherwise
	 */
	public boolean addReturnRecord(String customerID, String itemID, int numberOfItem) {
		int slot = getLocksSlot(customerID);
		//locks[slot].lock();
		ItemsList l = records[slot].get(customerID);
		if (l != null) {
			l.lock(itemID);
		}
		if (l == null ||
				l.getCount(itemID) < numberOfItem) {
			if (l != null) {
				l.unlock(itemID);
			}
			return false;
		}
		l.setCount(itemID, l.getCount(itemID) - numberOfItem);
		l.unlock(itemID);
		l.save(storageDir + File.separator + customerID);		
		//locks[slot].unlock();
		return true;
	}
	
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("=== Customer Records ===\n");
		for (int i = 0; i < HASHTABLE_SIZE; i++) {
			if (locks[i] != null && records[i] != null) {
				locks[i].lock();
				Iterator<String> it = records[i].keySet().iterator();
				while (it.hasNext()) {
					String key = it.next();
					sb.append("Customer: ");
					sb.append(key);
					sb.append("\n");
					sb.append(records[i].get(key).toString());
				}			
				locks[i].unlock();
			}
		}
		sb.append("\n");
		return sb.toString();
		
	}
	
	public void transferToStoreState(ReplicaState state) {
		for (int i = 0; i < HASHTABLE_SIZE; i++) {
			if (records[i] != null) {
				Iterator<String> it = records[i].keySet().iterator();
				while (it.hasNext()) {
					String customerID = it.next();
					ItemsList il = records[i].get(customerID);
					
					ReplicaState s = new ReplicaState();
					il.transferToStoreState(s);
					
					Map<String, Integer> items = s.getItems();
					Iterator<String> it2 = items.keySet().iterator();
					while (it2.hasNext()) {
						String itemID = it2.next();
						int numberOfItem = items.get(itemID).intValue();
						state.addCustomerBoughtItem(customerID, itemID, numberOfItem);
					}
				}			
			}
		}		
	}	

	public void transferFromStoreState(ReplicaState state) {
		init();
		Set<String> customers = state.getCustomers();
		Iterator<String> it = customers.iterator();		
		while (it.hasNext()) {
			String customerID = it.next();
			Map<String, Integer> items = state.getCustomerBoughtItems(customerID);
			Iterator<String> it2 = items.keySet().iterator();
			while (it2.hasNext()) {
				String itemID = it2.next();
				int numberOfItem = items.get(itemID).intValue();
				addSaleRecord(customerID, itemID, numberOfItem);
			}
		}
	}	
}
